# This Python file uses the following encoding: utf-8
############################################################################
# Copyright (c) 2025 Li Auto Inc. and its affiliates
# Licensed under the Apache License, Version 2.0(the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
############################################################################
import os
import can
import sys
import json
import time
import serial
import socket
import argparse
import multiprocessing
from typing import Dict
from datetime import datetime

from trace_data import IrqData, TaskData, CoreData, RunnableData
from trace_util import TraceCmdUtils, TraceInfoUtils, TraceComputeUtils, IDLE_TASK_PRIORITY, TraceSerialCmd
from trace_parser import VersionStr, VERSION_MAP, TRACE_DATA_PARSER
from ftrace_manager import FtraceProducer
from pftrace_manager import PftraceProducer


def get_input(msg: str = "") -> str:
    """获得键盘输入"""
    try:
        res = input(msg + " ")
    except EOFError:
        return ""

    return res


def clear():
    """Clears the console"""
    if os.name == 'nt':
        os.system('cls')
    else:
        os.system('clear')


class TraceMonitor(multiprocessing.Process):
    """主进程,管理每个device的子进程"""

    def __init__(self, device_conf_path, root_log_dir, stop_event, get_config_flag):
        super().__init__()
        self.device_conf_path = device_conf_path  # device_conf文件路径
        self.device_config_info: dict = {}  # {device id: device通信配置}
        self.device_process_dict: dict = {}  # {device id: device处理进程}
        self.device_saver_dict: dict = {}  # {device id: device保存进程}
        self.device_queue_dict: dict = {}  # {device id: device数据队列}
        self.init_log_dir(root_log_dir)
        self.stop_event = stop_event
        self.get_config_flag = get_config_flag

    def init_log_dir(self, root_log_dir):
        if root_log_dir == "":
            root_log_dir = "logs"
        time_suffix = datetime.now().strftime("%m_%d_%H%M%S")
        self.log_dir = os.path.join(root_log_dir, str(time_suffix))
        os.makedirs(self.log_dir, exist_ok=True)

    def start_device_process(self):
        """启动各device的处理进程"""
        for device_id, device_data in self.device_config_info.items():
            self.device_queue_dict[device_id] = multiprocessing.Queue()
            self.device_process_dict[device_id] = TraceDataProcess(self.device_queue_dict[device_id], device_data,
                                                                   self.log_dir, self.stop_event, self.get_config_flag)
            self.device_saver_dict[device_id] = TraceDataSaver(self.device_queue_dict[device_id], device_data,
                                                               self.log_dir, self.stop_event)
            self.device_process_dict[device_id].start()
            self.device_saver_dict[device_id].start()
        print("[INFO] All device processes have been launched.")

    def run(self):
        device_config_info = TraceInfoUtils.parse_device_conf(self.device_conf_path)
        if device_config_info is None:
            print("Please try again.")
            return
        self.device_config_info = device_config_info
        self.start_device_process()
        for device_id, device_process in self.device_process_dict.items():
            device_process.join()
        for device_id, device_saver in self.device_saver_dict.items():
            device_saver.join()


class TraceDataSaver(multiprocessing.Process):

    def __init__(self, original_data_queue, device_data, log_dir, stop_event) -> None:
        super().__init__()
        self.stop_event = stop_event
        self.original_data_queue = original_data_queue
        self.device_name = device_data["name"]
        self.init_saved_file(log_dir)

    def init_saved_file(self, log_dir):
        """初始化需要保存的文件路径"""
        os.makedirs(log_dir, exist_ok=True)
        self.data_file = os.path.join(log_dir, f"{self.device_name}_data.bin")  # trace二进制报文

    def run(self):
        while not self.stop_event.is_set():
            data = self.original_data_queue.get()
            if len(data) > 0:
                # 原始二进制数据文件
                with open(self.data_file, "ab") as f:
                    f.write(data)
                    f.flush()


class TraceDataProcess(multiprocessing.Process):
    """trace路线报文收发"""

    def __init__(self, original_data_queue, device_data, log_dir, stop_event, get_config_flag) -> None:
        super().__init__()
        self.stop_event = stop_event
        self.get_config_flag = get_config_flag

        self.original_data_queue = original_data_queue
        self.device_data = device_data
        self.version = device_data["version_no"]
        self.device_name = device_data["name"]
        self.comm_mode = device_data["comm_mode"].lower()
        self.udp_handle = None
        self.can_handle = None
        self.serial_handle = None
        self.log_dir = log_dir

        self.last_time = 0
        self.timer_duration = 3
        self.data_byte_cnt = 0

        self.stm_frequency: int = 0  # STM时钟频率
        self.core_dict: Dict[int, CoreData] = {}  # {core id: core数据}
        self.task_dict: Dict[int, TaskData] = {}  # {task id: task数据}
        self.runnable_dict: Dict[int, RunnableData] = {}  # {runnable id: runnable数据}
        self.irq_dict: Dict[int, IrqData] = {}  # {irq id: irq数据}

    def timer_func(self):
        now_time = time.time()
        if (now_time - self.last_time) > self.timer_duration:
            print(f"Data's byte rate: {int(self.data_byte_cnt / (now_time - self.last_time))}/s")
            self.data_byte_cnt = 0
            self.last_time = now_time

    def save_config_info(self):
        """保存下位机配置信息"""
        os.makedirs(self.log_dir, exist_ok=True)
        config_file = os.path.join(self.log_dir, "config.json")  # device配置文件
        if os.path.exists(config_file) is False:
            data = {}
        else:
            with open(config_file, "r") as f:
                data = json.load(f)
        data[self.device_name] = {
            "device_id": self.device_data["device_id"],
            "stm_frequency": self.stm_frequency,
            "Task": {},
            "Runnable": {},
            "ISR": {},
            "version_no": self.version,
        }
        for task_id, item in self.task_dict.items():
            data[self.device_name]["Task"][task_id] = [item.name, item.priority, item.core_id]
        for measurement_id, item in self.runnable_dict.items():
            data[self.device_name]["Runnable"][measurement_id] = [item.name, item.task_id_list]
        for irq_id, item in self.irq_dict.items():
            data[self.device_name]["ISR"][irq_id] = item.name
        with open(config_file, "w") as f:
            json.dump(data, f, indent=4, ensure_ascii=False)
            f.flush()

        print(f"[INFO][{self.device_name}] Config info has been written to {config_file}.")

    def save_first_utc(self):
        """保存下位机第一帧trace报文的UTC时间"""
        os.makedirs(self.log_dir, exist_ok=True)
        config_file = os.path.join(self.log_dir, "config.json")  # device配置文件
        if os.path.exists(config_file) is False:
            data = {}
        else:
            with open(config_file, "r") as f:
                data = json.load(f)
        data.setdefault(self.device_name, {})["first_utc"] = int(time.time())
        with open(config_file, "w") as f:
            json.dump(data, f, indent=4, ensure_ascii=False)
            f.flush()

    def connect_device(self):
        """device建立通信连接"""
        if self.comm_mode == "eth":
            if self.udp_bind(self.device_data["ip"], self.device_data["port"]) is False:
                return False
        elif self.comm_mode == "can":
            canid_filters = [{
                "channel": self.device_data["can_channel"],
                "id": self.device_data["rx_id"],
                "is_std": True
            }]
            if self.connect_can(self.device_data["can_channel"], canid_filters) is False:
                return False
        print(f"[INFO][{self.device_name}] Establish connection successfully.")
        return True

    def udp_bind(self, ip_address: str, ip_port: int) -> bool:
        """建立UDP通信"""
        self.udp_handle = None
        print(f"[INFO][{self.device_name}] Start to init udp {ip_address}:{ip_port}.")
        self.udp_handle = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.udp_handle.settimeout(0.2)
        try:
            self.udp_handle.bind(("0.0.0.0", ip_port))
        except Exception as e:
            print(f"[ERROR][{self.device_name}] Failed to bind socket: {e}")
            self.udp_handle = None
            return False
        print(f"[INFO][{self.device_name}] Udp init successfully.")
        return True

    def connect_can(self, can_channel, canid_filters) -> bool:
        """建立CAN通信"""
        self.can_handle = None
        try:
            self.can_handle = can.Bus(
                bustype="libtosun", 
                configs=[{
                    # NOTE：波特率相关参数受下位机限制，暂不支持配置 
                    "FChannel": can_channel, 
                    "rate_baudrate": 500, 
                    "data_baudrate": 2000, 
                    "enable_120hm": True, 
                    "is_fd": True
                }],
                is_include_tx=True, 
                hwserial=b"", 
                filters=canid_filters
            )
        except ValueError as err:
            print(f"[ERROR] {str(err)}")
            return False
        else:
            return True

    def connect_serial(self) -> bool:
        """建立串口通信"""
        port, baudrate = self.device_data["serial_port"], self.device_data["serial_baudrate"]
        try:
            self.serial_handle = serial.Serial(port=port, baudrate=baudrate, timeout=0.1)
        except Exception as e:
            print(str(e))
            self.serial_handle = None
            print(f"[ERROR][{self.device_name}] '{port}' open failed, please check the '{port}'.")
            return False
        else:
            if not self.serial_handle.isOpen():
                self.serial_handle = None
                print(f"[ERROR][{self.device_name}] '{port}' open failed, please check the '{port}'.")
                return False
            print(f"[INFO][{self.device_name}] Open '{port}' successfully.")
            return True

    def send_message_by_serial(self, message):
        """串口发送报文"""
        if (self.serial_handle is None) or (not self.serial_handle.isOpen()):
            print(f"[WARNING][{self.device_name}] serial_handle is not open.")
            return
        self.serial_handle.write(message)
        time.sleep(0.1)

    def receive_message_by_serial(self, cnt, is_limited=True):
        """串口接收报文"""
        data = self.get_serial_info(cnt)
        if is_limited is False:
            message = bytearray()
            while len(data) > 0:
                message += data
                data = self.get_serial_info(cnt)
            return message
        else:
            return data

    def get_serial_info(self, max_cnt):
        serial_info = bytearray()
        if (self.serial_handle is None) or (not self.serial_handle.isOpen()):
            print("[WARNING] serial_handle is not open.")
            return serial_info
        cnt = 0
        while cnt < 5:
            data = bytearray()
            try:
                data = self.serial_handle.read(max_cnt)
            except Exception:
                pass
            finally:
                if len(data) == 0:
                    cnt += 1
                elif (len(serial_info) + len(data)) >= max_cnt:
                    serial_info += data
                    return serial_info
                else:
                    serial_info += data
                    cnt = 0
        return serial_info

    def send_message(self, message):
        """上位机给device发送报文"""
        if self.comm_mode == "eth":
            if self.udp_handle is None:
                print("[WARNING] udp_handle is None.")
                return
            self.udp_handle.sendto(message, (self.device_data["ip"], self.device_data["port"]))
        elif self.comm_mode == "can":
            if self.can_handle is None:
                print("[WARNING] can_handle is None.")
                return
            try:
                arbitration_id = self.device_data["tx_id"]
                msg = can.Message(
                    channel=self.device_data["can_channel"], 
                    arbitration_id=arbitration_id, 
                    is_extended_id=False,
                    is_remote_frame=False, 
                    dlc=64, 
                    data=message, 
                    is_fd=True
                )
                self.can_handle.send(msg)
            except ValueError as err:
                print(f"[ERROR] {str(err)}")

    def receive_message(self, cnt, is_limited=True):
        """上位机接收device的报文"""
        if self.comm_mode == "eth":
            data = self.get_udp_info(cnt, self.device_data["ip"])
            if is_limited is False:
                message = bytearray()
                while len(data) > 0:
                    message += data
                    data = self.get_udp_info(cnt, self.device_data["ip"])
                return message
            else:
                return data
        elif self.comm_mode == "can":
            data = self.get_can_info(cnt)
            if is_limited is False:
                message = bytearray()
                while len(data) > 0:
                    message += data
                    data = self.get_can_info(cnt)
                return message
            else:
                return data

    def get_udp_info(self, max_cnt, ip_address):
        udp_info = bytearray()
        if self.udp_handle is None:
            print("[WARNING] udp_handle is None.")
            return udp_info
        cnt = 0
        while cnt < 5:
            data = bytearray()
            try:
                data, addr = self.udp_handle.recvfrom(max_cnt)
            except Exception:
                pass
            finally:
                if (len(data) == 0) or (addr[0] != ip_address):
                    cnt += 1
                elif (len(udp_info) + len(data)) >= max_cnt:
                    udp_info += data
                    return udp_info
                else:
                    udp_info += data
                    cnt = 0
        return udp_info

    def get_can_info(self, max_cnt):
        can_info = bytearray()
        if self.can_handle is None:
            print("[WARNING] can_handle is None.")
            return can_info
        cnt = 0
        while cnt < 5:
            try:
                can_msg, _ = self.can_handle.recv(channel=self.device_data["can_channel"], timeout=0.1)
            except Exception:
                pass
            finally:
                if can_msg is None or len(can_msg.data) == 0:
                    cnt += 1
                elif len(can_info) >= max_cnt:
                    can_info += can_msg.data[0:can_msg.dlc]
                    return can_info
                else:
                    can_info += can_msg.data[0:can_msg.dlc]
                    cnt = 0
        return can_info

    def get_config_data(self, cnt) -> bool:
        """通过握手获取下位机的配置报文"""
        if self.version == VERSION_MAP[VersionStr.VCOS_100.value]:
            # 获取下位机System Timer Frequency
            print(f"[INFO][{self.device_name}] Get System Timer Frequency...")
            self.send_message(TraceCmdUtils.get_ctrl_cmd(TraceCmdUtils.OTHER_INFO_TYPE))
            time.sleep(0.1)
            message = self.receive_message(cnt, False)
            res = TraceCmdUtils.parse_ctrl_response(message, self.device_data)
            if "stm_frequency" not in res:
                print(f"[ERROR][{self.device_name}] STM frequency message was not received. You can try again.")
                return False
            self.stm_frequency = res["stm_frequency"]
            # 获取下位机task信息
            print(f"[INFO][{self.device_name}] Get task information...")
            self.send_message(TraceCmdUtils.get_ctrl_cmd(TraceCmdUtils.TASK_INFO_TYPE))
            time.sleep(0.1)
            message = self.receive_message(cnt, False)
            res = TraceCmdUtils.parse_ctrl_response(message, self.device_data)
            if "task_info" not in res:
                print(f"[ERROR][{self.device_name}] Task info message was not received. You can try again.")
                return False
            core_dict = self.core_dict
            task_dict = self.task_dict
            for task_id, item in res["task_info"].items():
                core_id = item["core_id"]
                task_dict[task_id] = TaskData(name=item["name"], priority=item["priority"], core_id=core_id)
                if core_id not in core_dict:
                    core_dict[core_id] = CoreData()
                if task_id not in core_dict[core_id].task_id_list:
                    core_dict[core_id].task_id_list.append(task_id)
                if item["priority"] == IDLE_TASK_PRIORITY:
                    core_dict[core_id].idle_task_id = task_id
            task_dict = {key: task_dict[key] for key in sorted(task_dict)}
            self.task_dict = task_dict
            # 获取下位机runnable信息
            print(f"[INFO][{self.device_name}] Get runnable information...")
            self.send_message(TraceCmdUtils.get_ctrl_cmd(TraceCmdUtils.RUNNABLE_INFO_TYPE))
            time.sleep(0.1)
            message = self.receive_message(cnt, False)
            res = TraceCmdUtils.parse_ctrl_response(message, self.device_data)
            if "runnable_info" not in res:
                print(f"[WARNING][{self.device_name}] Runnable info message was not received.")
                res["runnable_info"] = {}
            for measurement_id, item in res["runnable_info"].items():
                self.runnable_dict[measurement_id] = RunnableData(name=item["name"],
                                                                period_time=item["period_time"],
                                                                task_id_list=item["task_id_list"])
            self.runnable_dict = {key: self.runnable_dict[key] for key in sorted(self.runnable_dict)}
            # 获取下位机ISR信息
            print(f"[INFO][{self.device_name}] Get ISR information...")
            self.send_message(TraceCmdUtils.get_ctrl_cmd(TraceCmdUtils.IRQ_INFO_TYPE))
            time.sleep(0.1)
            message = self.receive_message(cnt, False)
            res = TraceCmdUtils.parse_ctrl_response(message, self.device_data)
            if "irq_info" not in res:
                print(f"[ERROR][{self.device_name}] ISR info message was not received. You can try again.")
                return False
            irq_dict = self.irq_dict
            for irq_id, item in res["irq_info"].items():
                irq_dict[irq_id] = IrqData(name=item["name"])
                core_id = item["core_id"]
                if core_id not in core_dict:
                    core_dict[core_id] = CoreData()
                if irq_id not in core_dict[core_id].irq_id_list:
                    core_dict[core_id].irq_id_list.append(irq_id)
            irq_dict = {key: irq_dict[key] for key in sorted(irq_dict)}
            self.irq_dict = irq_dict
            self.core_dict = core_dict
        elif self.version == VERSION_MAP[VersionStr.VCOS_200.value]:
            trace_data_parser = TRACE_DATA_PARSER[self.version]
            self.stm_frequency = self.device_data["stm_frequency"]
            core_num = self.device_data["core_num"]
            for cur_core in range(core_num):
                self.send_message_by_serial(f"sc {cur_core}\n".encode())
                serial_info = self.receive_message_by_serial(1024, True)
                # 获取下位机task信息
                print(f"[INFO][{self.device_name}] Get core {cur_core}'s task information...")
                self.send_message_by_serial(TraceSerialCmd.SHAKE_HAND_TYPE)
                message = self.receive_message(cnt, False)
                print(len(message))
                res = TraceCmdUtils.parse_ctrl_response(message, self.device_data)
                if "task_info" not in res:
                    print(f"[ERROR][{self.device_name}] core {cur_core}'s task info message was not received. \
You can try again.")
                    return False
                core_dict = self.core_dict
                task_dict = self.task_dict
                for task_id, item in res["task_info"].items():
                    core_id = item["core_id"]
                    task_id = trace_data_parser.get_mapped_id(task_id, core_id)
                    task_dict[task_id] = TaskData(name=item["name"], priority=item["priority"], core_id=core_id)

                    if core_id not in core_dict:
                        core_dict[core_id] = CoreData()
                    if task_id not in core_dict[core_id].task_id_list:
                        core_dict[core_id].task_id_list.append(task_id)
                    if item["priority"] == IDLE_TASK_PRIORITY:
                        core_dict[core_id].idle_task_id = task_id
                task_dict = {key: task_dict[key] for key in sorted(task_dict)}
                self.task_dict = task_dict
                self.core_dict = core_dict
        self.send_message_by_serial(b"sc 0\n")
        print(f"[INFO][{self.device_name}] Switch to Core 0.")
        # 保存
        print(f"[INFO][{self.device_name}] Received all config info successfully.")
        self.save_config_info()
        return True

    def run(self, cnt=4096):
        if self.connect_device() is False:
            print("Please try again.")
            return
        if self.version == VERSION_MAP[VersionStr.VCOS_200.value]:
            if self.connect_serial() is False:
                print("Please try again.")
                return
        # 关闭下位机的所有trace事件,打开trace连接
        print(f"[INFO][{self.device_name}] Close Trace events and open connection.")
        if self.version == VERSION_MAP[VersionStr.VCOS_100.value]:
            close_event_cmd_cnt, open_connection_cmd_cnt = 1, 2
            self.send_message(
                TraceCmdUtils.get_ctrl_cmd(TraceCmdUtils.CLOSE_ALL_EVENTS_TYPE, cmd_cnt=close_event_cmd_cnt) +
                TraceCmdUtils.get_ctrl_cmd(TraceCmdUtils.OPEN_CONNECTION_TYPE, cmd_cnt=open_connection_cmd_cnt))
        elif self.version == VERSION_MAP[VersionStr.VCOS_200.value]:
            self.send_message_by_serial(TraceSerialCmd.CLOSE_ALL_EVENTS_TYPE)
            self.send_message_by_serial(TraceSerialCmd.CLOSE_CONNECTION_TYPE)
        # 握手等待
        if self.version == VERSION_MAP[VersionStr.VCOS_100.value]:
            time.sleep(0.1)
            message = self.receive_message(cnt, False)
            if len(message) == 0:
                print(f"[ERROR][{self.device_name}] Device message was not received. \
You can reset the board and try again.")
                return
            res = TraceCmdUtils.parse_ctrl_response(message, self.device_data)
            if ("cmd_status" not in res) or (close_event_cmd_cnt not in res["cmd_status"]) or \
                (open_connection_cmd_cnt not in res["cmd_status"]):
                print(f"[ERROR][{self.device_name}] Close_all_events or open_connection message was not received. \
You can try again.")
                return
        # 握手
        if self.get_config_flag is True:
            if self.get_config_data(cnt) is False:
                return

        self.save_first_utc()
        self.last_time = time.time()
        # ETH 一直接收数据
        if self.comm_mode == "eth":
            # 开启下位机的所有trace事件
            print(f"[INFO][{self.device_name}] Open all Trace events.")
            if self.version == VERSION_MAP[VersionStr.VCOS_100.value]:
                self.send_message(TraceCmdUtils.get_ctrl_cmd(TraceCmdUtils.OPEN_ALL_EVENTS_TYPE))
            elif self.version == VERSION_MAP[VersionStr.VCOS_200.value]:
                self.send_message_by_serial(TraceSerialCmd.OPEN_CONNECTION_TYPE)
                self.send_message_by_serial(TraceSerialCmd.OPEN_ALL_EVENTS_TYPE)
            # 接收原始数据
            print(f"[INFO][{self.device_name}] Start to receive trace data by udp...")
            while not self.stop_event.is_set():
                data = bytearray()
                try:
                    data = self.receive_message(cnt, True)
                # 无数据时会有超时error
                except Exception:
                    pass
                finally:
                    if data != b"":
                        self.data_byte_cnt += len(data)
                        self.original_data_queue.put(data)
                        self.timer_func()
        # NOTE: CAN只接收一次数据，数据量为下位机缓存池容量
        elif self.comm_mode == "can":
            # 关闭trace连接，开启下位机的所有trace事件
            print(f"[INFO][{self.device_name}] Close connection and open all Trace events.")
            self.send_message(TraceCmdUtils.get_ctrl_cmd(TraceCmdUtils.CLOSE_CONNECTION_TYPE) +
                            TraceCmdUtils.get_ctrl_cmd(TraceCmdUtils.OPEN_ALL_EVENTS_TYPE))
            # NOTE：当前CAN不支持连续发送，这里等待缓存池满  
            time.sleep(5)
            # 关闭下位机的所有trace事件，开启trace连接
            self.send_message(TraceCmdUtils.get_ctrl_cmd(TraceCmdUtils.CLOSE_ALL_EVENTS_TYPE) +
                            TraceCmdUtils.get_ctrl_cmd(TraceCmdUtils.OPEN_CONNECTION_TYPE))
            print(f"[INFO][{self.device_name}] Start to receive trace data by {self.comm_mode}...")
            msg_cnt = 0
            while msg_cnt < 10:
                data = bytearray()
                try:
                    data = self.receive_message(cnt, True)
                # 无数据时会有超时error
                except Exception:
                    pass
                finally:
                    if data != b"":
                        self.data_byte_cnt += len(data)
                        self.original_data_queue.put(data)
                        msg_cnt = 0
                        self.timer_func()
                    else:
                        msg_cnt += 1
            print(f"[INFO][{self.device_name}] Can data has all been received.")
            os._exit(0)

        # 结束前关闭trace event产生
        if self.version == VERSION_MAP[VersionStr.VCOS_100.value]:
            self.send_message(TraceCmdUtils.get_ctrl_cmd(TraceCmdUtils.CLOSE_ALL_EVENTS_TYPE) + 
                              TraceCmdUtils.get_ctrl_cmd(TraceCmdUtils.CLOSE_CONNECTION_TYPE))
        elif self.version == VERSION_MAP[VersionStr.VCOS_200.value]:
            self.send_message_by_serial(TraceSerialCmd.CLOSE_ALL_EVENTS_TYPE)
            self.send_message_by_serial(TraceSerialCmd.CLOSE_CONNECTION_TYPE)


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("-mode",
                        "-m",
                        type=str,
                        default="online",
                        choices=["online", "bin_to_ftrace", "bin_to_pftrace"],
                        help="")
    parser.add_argument("-device_conf_path", "-device", type=str, default="device_conf.json", help="")
    parser.add_argument("-log_dir", "-log", type=str, default="logs", help="")
    parser.add_argument("-get_config", "-get_config", type=str, default="y", choices=["y", "n"], help="")
    parser.add_argument("-data_dir", "-data", type=str, default="", help="")
    parser.add_argument("-event_chain_conf_path", "-chain", type=str, default="", help="")
    parser.add_argument("-time_format", "-time_format", type=str, default="local_tick", 
                        choices=["local_tick"], help="")
    parser.add_argument("-get_report", "-get_report", type=str, default="n", choices=["y", "n"], help="")
    args = parser.parse_args()

    mode = args.mode
    start_time = time.time()
    if mode == "online":
        device_conf_path = args.device_conf_path
        log_dir = args.log_dir
        get_config = args.get_config 
        if get_config == "y":
            get_config_flag = True
        else:
            get_config_flag = False
        print(f"[INFO] You set the parameters:\ndevice_conf_path: {device_conf_path},\n\
log_dir: {log_dir}\nget_config: {get_config}")
        print("------------------------------------------------------------------------")
        stop_event = multiprocessing.Event()
        trace_monitor = TraceMonitor(device_conf_path, log_dir, stop_event, get_config_flag)
        trace_monitor.start()
        while True:
            input_text = get_input().strip()
            if input_text == "exit":
                stop_event.set()
                trace_monitor.terminate()
                sys.exit()
            if input_text == "clear" or input_text == "cls":
                clear()
                continue
            time.sleep(1)
    elif mode == "bin_to_ftrace":
        data_dir = args.data_dir
        FtraceProducer().produce_ftrace_file(data_dir)
    elif mode == "bin_to_pftrace":
        data_dir = args.data_dir
        event_chain_conf_path = args.event_chain_conf_path
        time_format = args.time_format
        get_report = args.get_report
        print(f"[INFO] You set the parameters:\ndata_dir: {data_dir},\nevent_chain_conf_path: {event_chain_conf_path}\n\
time_format: {time_format}\nget_report: {get_report}")
        print("------------------------------------------------------------------------")
        PftraceProducer().produce_pftrace_file(data_dir, event_chain_conf_path, time_format=time_format)
        if get_report == "y":
            TraceComputeUtils.produce_statistics_file(data_dir)
    delta_time = time.time() - start_time
    print(f"[INFO] cost time {delta_time} s")
